import greenfoot.*;  
import java.awt.Color;
import javax.swing.event.EventListenerList;
import java.util.EventObject;

/**
 * Superclass for all four minigames. 
 * Contains functionality for all shared game behaviour
 * Also contains private class GameState to enable maintaining gamestate through out all the game
 * @author Jasper Jans, Andre de Zwart
 */
public class MiniGame extends World
{
    
    private long timeStarted = System.currentTimeMillis();  // internal timer 
    private GameStatus gameStatus;  // 
    private boolean countingDown = false;

    // in-game labels
    private Label lblTimer;
    private Label lblScore;
    private Label lblLifesLeft;
    private Label lblLevel;
    
    // in-game label templates
    private static String scoreString = "Score: %06d";
    private static String lifesLeftString = "Levens: %s";
    private static String levelString = "Level: %s";
    
    public GameStatus getGameStatus()
    {
        return gameStatus;
    }
    
    public void act()
    {
        if(Greenfoot.isKeyDown("b"))
        {
            Greenfoot.setWorld(new TitleScreen());
        }
        
        if(Greenfoot.isKeyDown("f"))
        {
            gameWon();
        }
        
        if(Greenfoot.isKeyDown("g"))
        {
            gameOver();
        }
        
        /* easier development 
        if(Greenfoot.isKeyDown("6"))
        {
            GameState.addSeconds(15);
        }
  
        if(Greenfoot.isKeyDown("7"))
        {
            addScore(100);
        }
        
        if(Greenfoot.isKeyDown("8"))
        {
            addLife();
        }
        
        if(Greenfoot.isKeyDown("9"))
        {
            substractLife();
        }
        */
        
       // player ran out of time, but has lifes left
        if(gameStatus == GameStatus.OUTOFTIME && Greenfoot.isKeyDown("space"))
        {
            substractLife();
            reset();
        }
        
 
        
        // game time is counting down
        if(countingDown)
        {
            long currentTime = System.currentTimeMillis();
            long elapsedTime = currentTime - timeStarted;
            
            
            if(elapsedTime / 1000 >= 1)
            {
                substractSecond();
                timeStarted = currentTime;
            }
       
            // out of time
            if(GameState.getSecondsLeft() == 0)
            {
                if(GameState.getLifesLeft() > 0)
                {
                    outOfTime();    // out of time, better luck next life
                }else
                {
                    gameOver();     // no more lifes left, that's gameover
                }
                countingDown = false;
            }
        }
        
        // gane won,convert remaining time to points
        if(gameStatus == GameStatus.WON)
        {
            if(GameState.getSecondsLeft() > 0)
            {
                convertRemainingTimeToPoints();
            }   
            else{
                Label continu = new Label(this, 110, 350);
                continu.setText("Druk op \"spatie\" om verder te gaan", 40);
                gameStatus = GameStatus.LEVELFINISHED;
            }
        }
        
       // game won and finished up, press space to go back to GameMap
        if(gameStatus == GameStatus.LEVELFINISHED && Greenfoot.isKeyDown("space"))
        {
            Greenfoot.setWorld(new GameMap(false));
        }
    }
    
    /*
     * Reset the current game
     */
    private void reset()
    {
        if(this instanceof SandBags)
        {
           Greenfoot.setWorld(new SandBags()); 
        }
        
        if(this instanceof PipeLines)
        {
            Greenfoot.setWorld(new PipeLines());
        }
        
        if(this instanceof EmergencyCenter)
        {
            Greenfoot.setWorld(new EmergencyCenter());
        }
        
        if(this instanceof RescueBoats)
        {
            Greenfoot.setWorld(new RescueBoats());
        } 
    }
    
    
    private void substractSecond()
    {
        GameState.substractSecond();
        lblTimer.setText(GameState.getTimeLeftAsString());

    }
   
    
    /**
     * Contructor for MiniGame
     * @param allowNumberOfSecondsForGame is the time in seconds the user gets at the start of the game
     * @param level is the current level number
     */
    public MiniGame(int allowedNumberOfSecondsForGame, int level)
    {    
        super(800, 600, 1); 
        Greenfoot.setSpeed(48);
        
        if(level >  0)
        {
            GameState.setSeconds(allowedNumberOfSecondsForGame);
            GameState.setLevel(level);
            
            gameStatus = GameStatus.PLAYING;
          
            lblTimer = new Label(this, 20, 10);        
            lblScore = new Label(this, 200, 10);
            lblLifesLeft = new Label(this, 500, 10);
            lblLevel = new Label(this, 700, 10);
            
            lblTimer.setText(GameState.getTimeLeftAsString());
    
            refreshLabels();
            
            countingDown = true;
        }
    }
    
    /*
     * Resets all gamesession data (score, lifes)
     */
    protected void startNewGame()
    {
        GameState.resetState();
    }
    
    protected int getLifes()
    {
        return GameState.getLifesLeft();
    }
   
    protected void addLife()
    {
        GameState.addLife();
        refreshLabels();
    }
    
    /*
     * Substract a life and checks if the gamer is gameover
     */
    protected void substractLife()
    {
        if(GameState.getLifesLeft() == 0)
        {
            gameOver();
        }else{
            GameState.substractLife();
            refreshLabels();
        }
     
    }
    
    protected void addScore(int score)
    {
        GameState.addScore(score);
        refreshLabels();
    }
    
    protected int getScore()
    {
        return GameState.getScore();
    }
    
    protected void addSeconds(int seconds)
    {
        GameState.addSeconds(seconds);
        refreshLabels();
    }
    
    protected int getSeconds()
    {
        return GameState.getSecondsLeft();
    }

    
    private void refreshLabels()
    {
        lblLifesLeft.setText(String.format(lifesLeftString,GameState.getLifesLeft()));
        lblScore.setText(String.format(scoreString, GameState.getScore()));
        lblLevel.setText(String.format(levelString, GameState.getLevel()));
    }
    
    /*
     * Stops timers, adds label on screen telling time's out and sets 
     * gamestatus to GameStatus.OUTOFTIME
     */
    private void outOfTime()
    {
        Label outOfTime = new Label(this, 280, 300);
        outOfTime.setText("Je tijd is op", 40);
        
        Label retry = new Label(this, 30, 350);
        retry.setText("Druk op \"spatie\" om nog een keer te proberen", 40);
        
        countingDown = false;
        gameStatus = GameStatus.OUTOFTIME;
    }
    
    /*
     * returns the number of the first game to play 
     */
    protected int getNextUnCompletedLevel()
    {
        
       // Loop through levels and return first unfinished 
       for(int i = 0; i <=3;i++)
            if(!GameState.getCompletedLevels()[i])
                return i+1;
              
       return 0;

    }
    
    /*
     * Adds labels on screen telling game over and sets gamestatus to gamestatus.GAMEOVER
     */
    private void gameOver()
    {
        Label gameOver = new Label(this, 300, 300);
        gameOver.setText("Game over", 40);
        
        countingDown = false;
        gameStatus = GameStatus.GAMEOVER;  
    }
    
    /*
     * Adds labels on screen telling game won and sets gamestatus to gamestatus.GAMEWON
     */
    public void gameWon()
    {
        
        GameState.getCompletedLevels()[GameState.getLevel()-1] = true;  // set current game to completed
        
        Label gameWonLabel = new Label(this, 280, 300);
        gameWonLabel.setText("Level voltooid!!!", 40);
         
        countingDown = false;
        gameStatus = GameStatus.WON;
    }
    
    /*
     * Sets gamestatus to GameStatus.GAMEOVER
     */
    public void gameEnd()
    {
        gameStatus = GameStatus.GAMEOVER;
    }
    
    /*
     * Converts a remaining to 25 points
     */
    private void convertRemainingTimeToPoints()
    {
        addScore(25);
        substractSecond();
    }
    
    /*
     * Returns true if the level has been completed
     * @paramer Level, the level to check
     */
    protected boolean levelIsCompleted(int level)
    {
       return GameState.getCompletedLevels()[level-1];
    }
    
    /*
     * GameState class maintains the sessionsdata of the current game
     */
    private static class GameState 
    {
        private static int _lifes = 3;
        private static int _score = 0;
        private static int _secondsLeft;
        private static int _level = 0;

        private static boolean[] completedLevels = new boolean[]{false,false,false,false};
        /*
         * addLife - gives the player an extra life
         */
        protected static void addLife()
        {
            _lifes++;
        }
        
        /*
         * getCompletedLevels - returns the completedLevels array
         */
        protected static boolean[] getCompletedLevels()
        {
            return completedLevels;
        }
        
        /*
         * substractLife - if player has at least one life left this function will substract it
         */
        protected static void substractLife()
        {
            if(_lifes >= 1)
            {
                _lifes--;
            }
        }
        
        /*
         * getLifesLeft - returns the amount of lifes left
         */
        protected static int getLifesLeft()
        {
           return _lifes;
        }
        
        protected static void setLevel(int level)
        {
            _level = level;
        }
        
        protected static int getLevel()
        {
            return _level;
        }
        
        protected static int getScore()
        {
            return _score;
        }
        
        protected static void addScore(int score)
        {
            _score += score;
        }
                
        protected static void substractSecond()
        {   
            _secondsLeft--;
        }
        
        protected static void addSeconds(int seconds)
        {
            _secondsLeft += seconds;
        }
        
        protected static void setSeconds(int seconds)
        {
            _secondsLeft = seconds;
        }
        
        protected static int getSecondsLeft()
        {
            return _secondsLeft;
        }
        
        /*
         * Resets all current session data
         */
        protected static void resetState()
        {
            _score = 0;
            _level = 0;
            _lifes = 3;
            
            for(int i = 0;i <= 3;i++)
                completedLevels[i] = false;
        }
        
        /*
         * Returns the amount of time as a string
         */
        protected static String getTimeLeftAsString()
        {
            String result;
            int minutes, seconds;
            
            if(_secondsLeft <= 60)
                result = Integer.toString(_secondsLeft); 
            
            minutes = _secondsLeft / 60;
            seconds = (_secondsLeft) % 60;
            result = String.format("%d:%02d", minutes, seconds);
            
            return result;
            
        }
    }
}

